#ifndef SP_H_INCLUDED
#define SP_H_INCLUDED

#include <iostream>
#include <vector>
using namespace std;

// cls SP
template <typename T>
class SP
{
public:
    SP() : ptr(nullptr), use(nullptr) {}
    explicit SP(T *p) : ptr(p), use(new size_t(1)) {}
    SP(const SP &sp) : ptr(sp.ptr), use(sp.use)
    {
        if (use)
            ++*use;
    }
    SP &operator=(SP &);
    size_t getUse() { return *use; }

    ~SP();
    T &operator*() { return *ptr; }
    T &operator*() const { return *ptr; }
    T *operator->() const { return ptr; }

private:
    T *ptr;
    size_t *use;
};

template <typename T>
SP<T>::~SP()
{
    if (use && --*use == 0)
    {
        delete ptr;
        delete use;
        ptr = nullptr;
        use = nullptr;
    }
}

template <typename T>
SP<T> &SP<T>::operator=(SP &rhs)
{
    if (rhs.use)
        ++*rhs.use;
    if (use && --*use == 0)
    {
        delete ptr;
        ptr = nullptr;
        delete use;
        use = nullptr;
    }
    ptr = rhs.ptr;
    use = rhs.use;
    return *this;
}

// make it
template <typename T, typename... Args>
SP<T> make_SP(Args &&...args)
{
    return SP<T>(new T(std::forward<Args>(args)...));
}

// cls UP

template <typename T>
class UP
{
public:
    UP() : ptr(nullptr) {}
    UP(T *p) : ptr(p) {}
    UP(const UP &) = delete;
    UP &operator=(const UP &) = delete;
    UP(UP &&up) : ptr(up.ptr) { up.ptr = nullptr; }
    UP &operator=(UP &&up)
    {
        if (this != &up)
        {
            delete ptr;
            ptr = up.ptr;
            up.ptr = nullptr;
        }
        return *this;
    }

    ~UP();
    T *release();
    void reset(T *newPtr = nullptr);
    T &operator*() { return *ptr; }
    T &operator*() const { return *ptr; }

private:
    T *ptr;
};

template <typename T>
UP<T>::~UP()
{
    if (ptr)
        delete ptr;
}

template <typename T>
T *UP<T>::release()
{
    T *temp = ptr;
    ptr = nullptr; // For this.class I release it.
    return temp;   // And give it to the caller
}

template <typename T>
void UP<T>::reset(T *newPtr)
{
    if (ptr)
        delete ptr;
    ptr = newPtr;
}

#endif

int main()
{
    SP<vector<int>> spv(new vector<int>);
    spv->push_back(8);
    vector<int> *vp = spv.operator->();
    cout << vp->front() << endl;

    SP<int> sp1(new int(10));
    cout << *sp1 << endl;
    cout << sp1.getUse() << endl;
    SP<int> sp2(sp1);
    cout << sp2.getUse() << endl;
    cout << sp1.getUse() << endl;
    sp2 = sp1;
    cout << sp2.getUse() << endl;
    cout << sp1.getUse() << endl;
    cout << *sp2 << endl;
    cout << *sp1 << endl;
    cout << "------------------------------------------" << endl;
    UP<int> up1(new int(20));
    cout << *up1 << endl;
    cout << "------------------------------------------" << endl;
    return 0;
}